package main

import (
	"fmt"
	"net"
	"os"
	"strings"
	"time"
)

//创建用户结构体类型
type Client struct {
	C    chan string
	Name string
	Addr string
}

//创建全局map存储在线用户
var onlineMap map[string]Client

//创建全局channel,用来传递用户消息
var message = make(chan string)

func main() {
	listener, err := net.Listen("tcp", "0.0.0.0:8000")
	LogErr(err, "net listen", true)
	defer listener.Close()

	//创建管理者go程,管理全局map和全局channel
	go Manager()

	//循环监听客户端连接请求
	for {
		conn, err := listener.Accept()
		LogErr(err, "listen accept",true)
		//启动go程处理客户端数据请求
		go GoHandleConnect(conn)
	}
}

func Manager() {
	//初始化onlinemap
	onlineMap = make(map[string]Client)

	for {
		//监听全局channel中是否有数据
		msg := <-message
		//循环发送消息给所有在线用户
		for _, v := range onlineMap {
			//把消息写入对应
			v.C <- msg
		}
	}
}

func GoHandleConnect(conn net.Conn) {
	defer conn.Close()

	//创建一个channel,判断用户是否活跃
	hasData := make(chan bool)

	//获取用户网络地址：ip+port
	netAddr := conn.RemoteAddr().String()
	//创建新连接用户的结构体,默认用户是ip+port
	cli := Client{
		make(chan string),
		netAddr,
		netAddr,
	}

	//将新连接用户,添加到在线用户map中
	onlineMap[netAddr] = cli
	fmt.Println(onlineMap)

	//创建专门给用户发消息的go程
	go WriteMsgToClient(cli, conn)

	//发送用户上线消息到全局channel中
	message <- MakeMsg(cli, "上线了")

	isQuit := make(chan bool)
	go func() {
		buf := make([]byte, 4096)
		for {
			n, err := conn.Read(buf)

			if n == 0 {
				fmt.Printf("检测到客户端:%s退出\n", cli.Name)
				isQuit <- true
				return
			}
			LogErr(err, "conn read error",false)
			msg := string(buf[:n-1])

			if msg == "who" {
				str := "online user:\n"
				for _, v := range onlineMap {
					str += v.Addr + ":" + v.Name + "\n"
				}
				cli.C <- str
			} else if msg == "" {
				cli.C <- "请输入要发送的内容"
			} else if len(msg) >= 8 && msg[:7] == "rename|" {
				arr := strings.Split(msg, "|")
				fmt.Println(arr)
				if arr[1] == "" {
					cli.C <- "请输入改后的名字"
				}
				fmt.Println(arr[1])
				cli.Name = arr[1]
				onlineMap[cli.Addr] = cli
				cli.C <- "修改名字成功,修改后的名字：" + arr[1]
			} else if msg == "exit" {
				isQuit <- true
			} else {
				message <- MakeMsg(cli, msg)
			}
			hasData <- true
		}
	}()

	//保证不退出
	for {
		select {
		case <-isQuit:
			delete(onlineMap, cli.Addr)
			message <- MakeMsg(cli, cli.Name+"已退出聊天")
			conn.Close()
			return
		case <-hasData: //目的是重置下面case的计时器

		case <-time.After(time.Second * 10):
			delete(onlineMap, cli.Addr)
			message <- MakeMsg(cli, "logout")
			fmt.Println(fmt.Sprintf("用户%d秒内无活动，切断连接",10))
			return
		}
	}
}

//专门向客户端发送消息
func WriteMsgToClient(cli Client, conn net.Conn) {
	for msg := range cli.C {
		conn.Write([]byte(msg + "\n"))
	}
}

func MakeMsg(cli Client, msg string) (data string) {
	netAddr := cli.Addr
	buf := fmt.Sprintf("[%s]:%s发送消息:%s", netAddr, cli.Name, msg)
	return buf
}

func LogErr(err error, info string,isExit bool) {
	if err != nil {
		fmt.Println(info, err)
		if isExit {
			//return  //结束当前函数调用
			//runtime.Goexit()  //结束当前go程
			os.Exit(1)	//将当前进程结束
		}
	}
}
